﻿using NPOI.SS.UserModel;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Soul.XLS
{
    public class ExcelColumn
    {
        public string Code { get; private set; }
        public string Name { get; private set; }
        public int? RowSpan { get; private set; }
        public int? ColSpan { get; private set; }
        public ICollection<ExcelColumn> Items { get; set; } = new List<ExcelColumn>();
        
        internal int? ColumnIndex { get; set; }
        internal Action<ExcelCellStyle> DataStyle { get; set; }
        public Action<ExcelCellStyle> ColumnStyle { get; set; }
        internal ExcelCellStyle DataStyleValue { get; private set; }

        internal ExcelColumn(IEnumerable<ExcelColumn> items)
        {
            Items = items.ToList();
        }

        public ExcelColumn(string name)
        {
            Code = name;
            Name = name;
        }

        public ExcelColumn(string code, string name)
        {
            Code = code;
            Name = name;
        }

        public ExcelColumn(string name, Action<ExcelCellStyle> style)
        {
            Code = name;
            Name = name;
            DataStyle = style;
        }

        public ExcelColumn(string code, string name, Action<ExcelCellStyle> style)
        {
            Code = code;
            Name = name;
            DataStyle = style;
        }

        public ExcelColumn(string name, int rowSpan, int colSpan)
        {
            Code = name;
            Name = name;
            RowSpan = rowSpan;
            ColSpan = colSpan;
        }

        public ExcelColumn(string code, string name, int rowSpan, int colSpan)
        {
            Code = code;
            Name = name;
            RowSpan = rowSpan;
            ColSpan = colSpan;
        }

        internal ExcelCellStyle ApplyColumnStyle(ExcelCellStyle style)
        {
            style.WrapText = true;
            style.BorderTop = BorderStyle.Thin;
            style.BorderBottom = BorderStyle.Thin;
            style.BorderLeft = BorderStyle.Thin;
            style.BorderRight = BorderStyle.Thin;
            style.Alignment = HorizontalAlignment.Center;
            style.VerticalAlignment = VerticalAlignment.Center;
            style.Font.IsBold = true;
            ColumnStyle?.Invoke(style);
            return style;
        }

        internal void ApplyDataStyle(IWorkbook workbook)
        {
            var style = workbook.CreeteExcelCellStyle();
            style.WrapText = true;
            style.BorderTop = BorderStyle.Thin;
            style.BorderBottom = BorderStyle.Thin;
            style.BorderLeft = BorderStyle.Thin;
            style.BorderRight = BorderStyle.Thin;
            style.Alignment = HorizontalAlignment.Center;
            style.VerticalAlignment = VerticalAlignment.Center;
            DataStyle?.Invoke(style);
            if (!string.IsNullOrEmpty(style.Format))
            {
                var format = workbook.CreateDataFormat();
                style.DataFormat = format.GetFormat(style.Format);
            }
            DataStyleValue = style;
        }

        internal int GetDepth()
        {
            if (Items == null || Items.Count == 0)
            {
                return 1;
            }
            int maxDepth = 0;
            foreach (var item in Items)
            {
                maxDepth = Math.Max(maxDepth, item.GetDepth());
            }
            return maxDepth + 1;
        }

        internal List<ExcelColumn> GetLeafNodes()
        {
            var nodes = new List<ExcelColumn>();
            GetLeafNodes(this, nodes);
            return nodes;
        }

        private void GetLeafNodes(ExcelColumn node, List<ExcelColumn> leaves)
        {
            if (node.Items == null || node.Items.Count == 0)
            {
                leaves.Add(node);
            }
            else
            {
                foreach (var item in node.Items)
                {
                    GetLeafNodes(item, leaves);
                }
            }
        }

        internal void CalcSpan()
        {
            var maxDepth = GetDepth();
            CalcSpan(this, 0, maxDepth);
        }

        private void CalcSpan(ExcelColumn node, int depth, int maxDepth)
        {
            if (node.RowSpan == null)
            {
                if (node.Items == null || !node.Items.Any())
                {
                    node.RowSpan = maxDepth - depth;
                }
                else
                {
                    node.RowSpan = 1;
                }
            }
            if (node.ColSpan == null)
            {
                node.ColSpan = node.GetLeafNodes().Count;
            }
            foreach (var item in node.Items)
            {
                CalcSpan(item, depth + 1, maxDepth);
            }
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
